package code.google.jcustomize;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.security.ProtectionDomain;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

class ConfigSource {
    private boolean DEBUG   = false;
    
    public ConfigSource() {
        DEBUG = "true".equals(System.getProperty("debug.configsource"));
    }

    public InputStream fileConfigRead(String path, boolean fspath) {
        if (fspath) {
            try {
                return new FileInputStream(path);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                return null;
            }
        } else {
            InputStream is = this.getClass().getResourceAsStream(path);
            if (is != null) return is;
            try {
                File dir = getJARDirectory();
                File res = new File(dir, path);
                if (DEBUG) System.out.println("Read: "+res.getAbsolutePath());
                return new FileInputStream(res);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public OutputStream fileConfigWrite(String path, boolean append, boolean fspath) throws FileNotFoundException {
        if (fspath) {
            try {
                return new FileOutputStream(path, append);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
                return null;
            }
        } else {
            URL url = this.getClass().getResource(path);
            OutputStream os = url != null ? new FileOutputStream(url.toString(), append) : null;
            if (os != null) return os;
            try {
                File dir = getJARDirectory();
                File res = new File(dir, path);
                if (DEBUG) System.out.println("Write: "+res.getAbsolutePath());
                return new FileOutputStream(res, append);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
    
    public String createTempFile(String path, String prefix, boolean fspath) {
        File temp = fspath ? new File(path) : new File(getJARDirectory(), path);
        try {
//            System.out.println(temp.getParentFile().toString());
            temp = File.createTempFile(prefix, null, temp.getParentFile());
            return temp.getAbsolutePath();
        } catch (IOException ioe) {
            ioe.printStackTrace();
            return null;
        }
    }
    
    public File getJARDirectory() {
        ProtectionDomain pd = this.getClass().getProtectionDomain();
        if (pd != null && pd.getCodeSource() != null && pd.getCodeSource().getLocation() != null) {
            URL jarURL = pd.getCodeSource().getLocation();
            try {
                File jar = new File(new URI(jarURL.toString()));
                File dir = jar.getParentFile();
                return dir;
            } catch (URISyntaxException e) {
                e.printStackTrace();
            }
        } else {
            if (DEBUG) System.out.println("Protection domain is null, so return null");
        }
        return null;
    }
    
    public boolean safeRename(String from, String to, boolean fspathFrom, boolean fspathTo) {
        File _from = null;
        File _to = null;
        File jarDir = fspathFrom && fspathTo ? null : getJARDirectory(); 
        _from = fspathFrom ? new File(from) : new File(jarDir, from);
        _to = fspathTo ? new File(to) : new File(jarDir, to);

        return Files.safeRename(_from, _to);
    }
    
    private DocumentBuilderFactory dbf          = null;
    private DocumentBuilder db                  = null;
    
    private EntityResolver dtdResolver          = 
    new EntityResolver() {
        String dtdDir   = null;
        File dir = null;
        {
            dtdDir = System.getProperty("dtdDir");
            if (dtdDir != null) dir = new File(dtdDir);
        }
        
        public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
            if (systemId == null) return null;
            if (DEBUG) System.out.println("SystemId='"+systemId+"', publicId='"+publicId+"'");
            if (systemId.startsWith("http://")) {
                if (systemId.endsWith(".dtd"))
                    try {
                        String filename = systemId.substring(systemId.lastIndexOf('/')+1);
                        InputSource local = resolveEntity(publicId, filename);
                        return local;
                    } catch (Exception e) {
                    }
                return null;
            }
            if (systemId.indexOf('/') >= 0 || systemId.indexOf('\\') >= 0) {
                try {
                    URI uri = new URI(systemId);
                    return new InputSource(new FileInputStream(new File(uri)));
                } catch (URISyntaxException e) {
                } catch (FileNotFoundException fnfe) {
                }
                systemId = systemId.substring(systemId.lastIndexOf('/')+1);
            }
            File dtd = dir == null ? new File(systemId) : new File(dir, systemId);
            if (DEBUG) System.out.println("InputSource location is '"+dtd.getAbsolutePath()+"'");
            return new InputSource(new FileInputStream(dtd));
        }
    };
    
    public Document readDocument(InputStream is) {
        try {
            if (dbf == null) {
                dbf = DocumentBuilderFactory.newInstance();
                dbf.setValidating(false);
                dbf.setNamespaceAware(true);
            }
            if (db == null) {
                db = dbf.newDocumentBuilder();
                db.setEntityResolver(dtdResolver);
            }
            return db.parse(is);
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}
